// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/macros.h"

#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

#if defined(OS_WIN)
#include <excpt.h>
#include <windows.h>
#endif // OS_WIN

namespace logging {

namespace {

    using ::testing::Return;

    // Needs to be global since log assert handlers can't maintain state.
    int log_sink_call_count = 0;

#if !defined(OFFICIAL_BUILD) || defined(DCHECK_ALWAYS_ON) || !defined(NDEBUG)
    void LogSink(const std::string& str)
    {
        ++log_sink_call_count;
    }
#endif

    // Class to make sure any manipulations we do to the min log level are
    // contained (i.e., do not affect other unit tests).
    class LogStateSaver {
    public:
        LogStateSaver()
            : old_min_log_level_(GetMinLogLevel())
        {
        }

        ~LogStateSaver()
        {
            SetMinLogLevel(old_min_log_level_);
            SetLogAssertHandler(NULL);
            log_sink_call_count = 0;
        }

    private:
        int old_min_log_level_;

        DISALLOW_COPY_AND_ASSIGN(LogStateSaver);
    };

    class LoggingTest : public testing::Test {
    private:
        LogStateSaver log_state_saver_;
    };

    class MockLogSource {
    public:
        MOCK_METHOD0(Log, const char*());
    };

    TEST_F(LoggingTest, BasicLogging)
    {
        MockLogSource mock_log_source;
        EXPECT_CALL(mock_log_source, Log()).Times(DEBUG_MODE ? 16 : 8).WillRepeatedly(Return("log message"));

        SetMinLogLevel(LOG_INFO);

        EXPECT_TRUE(LOG_IS_ON(INFO));
        // As of g++-4.5, the first argument to EXPECT_EQ cannot be a
        // constant expression.
        const bool kIsDebugMode = (DEBUG_MODE != 0);
        EXPECT_TRUE(kIsDebugMode == DLOG_IS_ON(INFO));
        EXPECT_TRUE(VLOG_IS_ON(0));

        LOG(INFO) << mock_log_source.Log();
        LOG_IF(INFO, true) << mock_log_source.Log();
        PLOG(INFO) << mock_log_source.Log();
        PLOG_IF(INFO, true) << mock_log_source.Log();
        VLOG(0) << mock_log_source.Log();
        VLOG_IF(0, true) << mock_log_source.Log();
        VPLOG(0) << mock_log_source.Log();
        VPLOG_IF(0, true) << mock_log_source.Log();

        DLOG(INFO) << mock_log_source.Log();
        DLOG_IF(INFO, true) << mock_log_source.Log();
        DPLOG(INFO) << mock_log_source.Log();
        DPLOG_IF(INFO, true) << mock_log_source.Log();
        DVLOG(0) << mock_log_source.Log();
        DVLOG_IF(0, true) << mock_log_source.Log();
        DVPLOG(0) << mock_log_source.Log();
        DVPLOG_IF(0, true) << mock_log_source.Log();
    }

    TEST_F(LoggingTest, LogIsOn)
    {
#if defined(NDEBUG)
        const bool kDfatalIsFatal = false;
#else // defined(NDEBUG)
        const bool kDfatalIsFatal = true;
#endif // defined(NDEBUG)

        SetMinLogLevel(LOG_INFO);
        EXPECT_TRUE(LOG_IS_ON(INFO));
        EXPECT_TRUE(LOG_IS_ON(WARNING));
        EXPECT_TRUE(LOG_IS_ON(ERROR));
        EXPECT_TRUE(LOG_IS_ON(FATAL));
        EXPECT_TRUE(LOG_IS_ON(DFATAL));

        SetMinLogLevel(LOG_WARNING);
        EXPECT_FALSE(LOG_IS_ON(INFO));
        EXPECT_TRUE(LOG_IS_ON(WARNING));
        EXPECT_TRUE(LOG_IS_ON(ERROR));
        EXPECT_TRUE(LOG_IS_ON(FATAL));
        EXPECT_TRUE(LOG_IS_ON(DFATAL));

        SetMinLogLevel(LOG_ERROR);
        EXPECT_FALSE(LOG_IS_ON(INFO));
        EXPECT_FALSE(LOG_IS_ON(WARNING));
        EXPECT_TRUE(LOG_IS_ON(ERROR));
        EXPECT_TRUE(LOG_IS_ON(FATAL));
        EXPECT_TRUE(LOG_IS_ON(DFATAL));

        // LOG_IS_ON(FATAL) should always be true.
        SetMinLogLevel(LOG_FATAL + 1);
        EXPECT_FALSE(LOG_IS_ON(INFO));
        EXPECT_FALSE(LOG_IS_ON(WARNING));
        EXPECT_FALSE(LOG_IS_ON(ERROR));
        EXPECT_TRUE(LOG_IS_ON(FATAL));
        EXPECT_TRUE(kDfatalIsFatal == LOG_IS_ON(DFATAL));
    }

    TEST_F(LoggingTest, LoggingIsLazyBySeverity)
    {
        MockLogSource mock_log_source;
        EXPECT_CALL(mock_log_source, Log()).Times(0);

        SetMinLogLevel(LOG_WARNING);

        EXPECT_FALSE(LOG_IS_ON(INFO));
        EXPECT_FALSE(DLOG_IS_ON(INFO));
        EXPECT_FALSE(VLOG_IS_ON(1));

        LOG(INFO) << mock_log_source.Log();
        LOG_IF(INFO, false) << mock_log_source.Log();
        PLOG(INFO) << mock_log_source.Log();
        PLOG_IF(INFO, false) << mock_log_source.Log();
        VLOG(1) << mock_log_source.Log();
        VLOG_IF(1, true) << mock_log_source.Log();
        VPLOG(1) << mock_log_source.Log();
        VPLOG_IF(1, true) << mock_log_source.Log();

        DLOG(INFO) << mock_log_source.Log();
        DLOG_IF(INFO, true) << mock_log_source.Log();
        DPLOG(INFO) << mock_log_source.Log();
        DPLOG_IF(INFO, true) << mock_log_source.Log();
        DVLOG(1) << mock_log_source.Log();
        DVLOG_IF(1, true) << mock_log_source.Log();
        DVPLOG(1) << mock_log_source.Log();
        DVPLOG_IF(1, true) << mock_log_source.Log();
    }

    TEST_F(LoggingTest, LoggingIsLazyByDestination)
    {
        MockLogSource mock_log_source;
        MockLogSource mock_log_source_error;
        EXPECT_CALL(mock_log_source, Log()).Times(0);

        // Severity >= ERROR is always printed to stderr.
        EXPECT_CALL(mock_log_source_error, Log()).Times(1).WillRepeatedly(Return("log message"));

        LoggingSettings settings;
        settings.logging_dest = LOG_NONE;
        InitLogging(settings);

        LOG(INFO) << mock_log_source.Log();
        LOG(WARNING) << mock_log_source.Log();
        LOG(ERROR) << mock_log_source_error.Log();
    }

// Official builds have CHECKs directly call BreakDebugger.
#if !defined(OFFICIAL_BUILD)

    TEST_F(LoggingTest, CheckStreamsAreLazy)
    {
        MockLogSource mock_log_source, uncalled_mock_log_source;
        EXPECT_CALL(mock_log_source, Log()).Times(8).WillRepeatedly(Return("check message"));
        EXPECT_CALL(uncalled_mock_log_source, Log()).Times(0);

        SetLogAssertHandler(&LogSink);

        CHECK(mock_log_source.Log()) << uncalled_mock_log_source.Log();
        PCHECK(!mock_log_source.Log()) << mock_log_source.Log();
        CHECK_EQ(mock_log_source.Log(), mock_log_source.Log())
            << uncalled_mock_log_source.Log();
        CHECK_NE(mock_log_source.Log(), mock_log_source.Log())
            << mock_log_source.Log();
    }

#endif

#if defined(OFFICIAL_BUILD) && defined(OS_WIN)
    NOINLINE void CheckContainingFunc(int death_location)
    {
        CHECK(death_location != 1);
        CHECK(death_location != 2);
        CHECK(death_location != 3);
    }

    int GetCheckExceptionData(EXCEPTION_POINTERS* p, DWORD* code, void** addr)
    {
        *code = p->ExceptionRecord->ExceptionCode;
        *addr = p->ExceptionRecord->ExceptionAddress;
        return EXCEPTION_EXECUTE_HANDLER;
    }

    TEST_F(LoggingTest, CheckCausesDistinctBreakpoints)
    {
        DWORD code1 = 0;
        DWORD code2 = 0;
        DWORD code3 = 0;
        void* addr1 = nullptr;
        void* addr2 = nullptr;
        void* addr3 = nullptr;

        // Record the exception code and addresses.
        __try {
            CheckContainingFunc(1);
        } __except (
            GetCheckExceptionData(GetExceptionInformation(), &code1, &addr1)) {
        }

        __try {
            CheckContainingFunc(2);
        } __except (
            GetCheckExceptionData(GetExceptionInformation(), &code2, &addr2)) {
        }

        __try {
            CheckContainingFunc(3);
        } __except (
            GetCheckExceptionData(GetExceptionInformation(), &code3, &addr3)) {
        }

        // Ensure that the exception codes are correct (in particular, breakpoints,
        // not access violations).
        EXPECT_EQ(STATUS_BREAKPOINT, code1);
        EXPECT_EQ(STATUS_BREAKPOINT, code2);
        EXPECT_EQ(STATUS_BREAKPOINT, code3);

        // Ensure that none of the CHECKs are colocated.
        EXPECT_NE(addr1, addr2);
        EXPECT_NE(addr1, addr3);
        EXPECT_NE(addr2, addr3);
    }
#endif // OFFICIAL_BUILD && OS_WIN

    TEST_F(LoggingTest, DebugLoggingReleaseBehavior)
    {
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
        int debug_only_variable = 1;
#endif
        // These should avoid emitting references to |debug_only_variable|
        // in release mode.
        DLOG_IF(INFO, debug_only_variable) << "test";
        DLOG_ASSERT(debug_only_variable) << "test";
        DPLOG_IF(INFO, debug_only_variable) << "test";
        DVLOG_IF(1, debug_only_variable) << "test";
    }

    TEST_F(LoggingTest, DcheckStreamsAreLazy)
    {
        MockLogSource mock_log_source;
        EXPECT_CALL(mock_log_source, Log()).Times(0);
#if DCHECK_IS_ON()
        DCHECK(true) << mock_log_source.Log();
        DCHECK_EQ(0, 0) << mock_log_source.Log();
#else
        DCHECK(mock_log_source.Log()) << mock_log_source.Log();
        DPCHECK(mock_log_source.Log()) << mock_log_source.Log();
        DCHECK_EQ(0, 0) << mock_log_source.Log();
        DCHECK_EQ(mock_log_source.Log(), static_cast<const char*>(NULL))
            << mock_log_source.Log();
#endif
    }

    void DcheckEmptyFunction1()
    {
        // Provide a body so that Release builds do not cause the compiler to
        // optimize DcheckEmptyFunction1 and DcheckEmptyFunction2 as a single
        // function, which breaks the Dcheck tests below.
        LOG(INFO) << "DcheckEmptyFunction1";
    }
    void DcheckEmptyFunction2() { }

    TEST_F(LoggingTest, Dcheck)
    {
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
        // Release build.
        EXPECT_FALSE(DCHECK_IS_ON());
        EXPECT_FALSE(DLOG_IS_ON(DCHECK));
#elif defined(NDEBUG) && defined(DCHECK_ALWAYS_ON)
        // Release build with real DCHECKS.
        SetLogAssertHandler(&LogSink);
        EXPECT_TRUE(DCHECK_IS_ON());
        EXPECT_TRUE(DLOG_IS_ON(DCHECK));
#else
        // Debug build.
        SetLogAssertHandler(&LogSink);
        EXPECT_TRUE(DCHECK_IS_ON());
        EXPECT_TRUE(DLOG_IS_ON(DCHECK));
#endif

        EXPECT_EQ(0, log_sink_call_count);
        DCHECK(false);
        EXPECT_EQ(DCHECK_IS_ON() ? 1 : 0, log_sink_call_count);
        DPCHECK(false);
        EXPECT_EQ(DCHECK_IS_ON() ? 2 : 0, log_sink_call_count);
        DCHECK_EQ(0, 1);
        EXPECT_EQ(DCHECK_IS_ON() ? 3 : 0, log_sink_call_count);

        // Test DCHECK on std::nullptr_t
        log_sink_call_count = 0;
        const void* p_null = nullptr;
        const void* p_not_null = &p_null;
        DCHECK_EQ(p_null, nullptr);
        DCHECK_EQ(nullptr, p_null);
        DCHECK_NE(p_not_null, nullptr);
        DCHECK_NE(nullptr, p_not_null);
        EXPECT_EQ(0, log_sink_call_count);

        // Test DCHECK on a scoped enum.
        enum class Animal { DOG,
            CAT };
        DCHECK_EQ(Animal::DOG, Animal::DOG);
        EXPECT_EQ(0, log_sink_call_count);
        DCHECK_EQ(Animal::DOG, Animal::CAT);
        EXPECT_EQ(DCHECK_IS_ON() ? 1 : 0, log_sink_call_count);

        // Test DCHECK on functions and function pointers.
        log_sink_call_count = 0;
        struct MemberFunctions {
            void MemberFunction1()
            {
                // See the comment in DcheckEmptyFunction1().
                LOG(INFO) << "Do not merge with MemberFunction2.";
            }
            void MemberFunction2() { }
        };
        void (MemberFunctions::*mp1)() = &MemberFunctions::MemberFunction1;
        void (MemberFunctions::*mp2)() = &MemberFunctions::MemberFunction2;
        void (*fp1)() = DcheckEmptyFunction1;
        void (*fp2)() = DcheckEmptyFunction2;
        void (*fp3)() = DcheckEmptyFunction1;
        DCHECK_EQ(fp1, fp3);
        EXPECT_EQ(0, log_sink_call_count);
        DCHECK_EQ(mp1, &MemberFunctions::MemberFunction1);
        EXPECT_EQ(0, log_sink_call_count);
        DCHECK_EQ(mp2, &MemberFunctions::MemberFunction2);
        EXPECT_EQ(0, log_sink_call_count);
        DCHECK_EQ(fp1, fp2);
        EXPECT_EQ(DCHECK_IS_ON() ? 1 : 0, log_sink_call_count);
        DCHECK_EQ(mp2, &MemberFunctions::MemberFunction1);
        EXPECT_EQ(DCHECK_IS_ON() ? 2 : 0, log_sink_call_count);
    }

    TEST_F(LoggingTest, DcheckReleaseBehavior)
    {
        int some_variable = 1;
        // These should still reference |some_variable| so we don't get
        // unused variable warnings.
        DCHECK(some_variable) << "test";
        DPCHECK(some_variable) << "test";
        DCHECK_EQ(some_variable, 1) << "test";
    }

    TEST_F(LoggingTest, DCheckEqStatements)
    {
        bool reached = false;
        if (false)
            DCHECK_EQ(false, true); // Unreached.
        else
            DCHECK_EQ(true, reached = true); // Reached, passed.
        ASSERT_EQ(DCHECK_IS_ON() ? true : false, reached);

        if (false)
            DCHECK_EQ(false, true); // Unreached.
    }

    TEST_F(LoggingTest, CheckEqStatements)
    {
        bool reached = false;
        if (false)
            CHECK_EQ(false, true); // Unreached.
        else
            CHECK_EQ(true, reached = true); // Reached, passed.
        ASSERT_TRUE(reached);

        if (false)
            CHECK_EQ(false, true); // Unreached.
    }

    // Test that defining an operator<< for a type in a namespace doesn't prevent
    // other code in that namespace from calling the operator<<(ostream, wstring)
    // defined by logging.h. This can fail if operator<<(ostream, wstring) can't be
    // found by ADL, since defining another operator<< prevents name lookup from
    // looking in the global namespace.
    namespace nested_test {
        class Streamable {
        };
        ALLOW_UNUSED_TYPE std::ostream& operator<<(std::ostream& out,
            const Streamable&)
        {
            return out << "Streamable";
        }
        TEST_F(LoggingTest, StreamingWstringFindsCorrectOperator)
        {
            std::wstring wstr = L"Hello World";
            std::ostringstream ostr;
            ostr << wstr;
            EXPECT_EQ("Hello World", ostr.str());
        }
    } // namespace nested_test

} // namespace

} // namespace logging
